]> dgit.raspbian.org Git - nodejs.git/commitdiff
dns,net: reject hostnames with embedded NUL bytes
authorMatteo Collina <hello@matteocollina.com>
Mon, 11 May 2026 07:29:15 +0000 (09:29 +0200)
committerBastien Roucariès <rouca@debian.org>
Sun, 16 Aug 2026 17:34:06 +0000 (19:34 +0200)
Ref: https://hackerone.com/reports/3656716
PR-URL: https://github.com/nodejs-private/node-private/pull/868
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
CVE-ID: CVE-2026-48930
Refs: https://hackerone.com/reports/3656716
origin: backport, https://github.com/nodejs/node/commit/c551a51d0c58dfc91961fb3f24c2c86af6183eca
Bug: https://nodejs.org/en/blog/vulnerability/june-2026-security-releases#embedded-nul-hostnames-can-lead-to-silent-authority-rebinding-due-to-c-string-trunc

Gbp-Pq: Topic sec
Gbp-Pq: Name CVE-2026-48930.patch

lib/dns.js
lib/internal/dns/promises.js
lib/internal/validators.js
lib/net.js
test/parallel/test-dns-lookup.js
test/parallel/test-net-connect-options-invalid.js

index ca932ad05f4da7e6cd59f9dd35c3f28b0f216be6..892d3bf8162ec6701978d47066dd656d09d5daad 100644 (file)
@@ -81,7 +81,7 @@ const {
   validateNumber,
   validateOneOf,
   validatePort,
-  validateString,
+  validateStringWithoutNullBytes,
 } = require('internal/validators');
 
 const {
@@ -145,7 +145,7 @@ function lookup(hostname, options, callback) {
 
   // Parse arguments
   if (hostname) {
-    validateString(hostname, 'hostname');
+    validateStringWithoutNullBytes(hostname, 'hostname');
   }
 
   if (typeof options === 'function') {
index 1169b2735d4efe099efe7112b0f2bc8f69aa2b7b..3fede4f8218e6757db55db978c7ac67407fa10b1 100644 (file)
@@ -65,6 +65,7 @@ const {
   validateOneOf,
   validatePort,
   validateString,
+  validateStringWithoutNullBytes,
 } = require('internal/validators');
 
 const kPerfHooksDnsLookupContext = Symbol('kPerfHooksDnsLookupContext');
@@ -186,7 +187,7 @@ function lookup(hostname, options) {
 
   // Parse arguments
   if (hostname) {
-    validateString(hostname, 'hostname');
+    validateStringWithoutNullBytes(hostname, 'hostname');
   }
 
   if (typeof options === 'number') {
index 2f24f6f781ea4d164efc779ba2f5e06b714885a2..b860006dff02d5730037d08286973685180dfc1c 100644 (file)
@@ -15,6 +15,7 @@ const {
   ObjectPrototypeHasOwnProperty,
   RegExpPrototypeExec,
   String,
+  StringPrototypeIncludes,
   StringPrototypeToUpperCase,
   StringPrototypeTrim,
 } = primordials;
@@ -162,6 +163,14 @@ function validateString(value, name) {
     throw new ERR_INVALID_ARG_TYPE(name, 'string', value);
 }
 
+/** @type {validateString} */
+const validateStringWithoutNullBytes = hideStackFrames((value, name) => {
+  validateString(value, name);
+  if (StringPrototypeIncludes(value, '\u0000')) {
+    throw new ERR_INVALID_ARG_VALUE(name, value, 'must be a string without null bytes');
+  }
+});
+
 /**
  * @callback validateNumber
  * @param {*} value
@@ -563,6 +572,7 @@ module.exports = {
   validatePort,
   validateSignalName,
   validateString,
+  validateStringWithoutNullBytes,
   validateUint32,
   validateUndefined,
   validateUnion,
index be4a43589394e5b33ee6d6538687fd8a32867868..d4e7250844a2feba3eb31beac83e2aef7cf7b880 100644 (file)
@@ -122,6 +122,7 @@ const {
   validateNumber,
   validatePort,
   validateString,
+  validateStringWithoutNullBytes,
 } = require('internal/validators');
 const kLastWriteQueueSize = Symbol('lastWriteQueueSize');
 const {
@@ -1265,6 +1266,8 @@ function lookupAndConnect(self, options) {
   const host = options.host || 'localhost';
   let { port, autoSelectFamilyAttemptTimeout, autoSelectFamily } = options;
 
+  validateStringWithoutNullBytes(host, 'options.host');
+
   if (localAddress && !isIP(localAddress)) {
     throw new ERR_INVALID_IP_ADDRESS(localAddress);
   }
index a847a91d655196f6501051da8d5fde439af2740a..56c55592d694e73918db0a3a1d3e510dc3e926ec 100644 (file)
@@ -23,6 +23,17 @@ const dnsPromises = dns.promises;
   assert.throws(() => dnsPromises.lookup(1, {}), err);
 }
 
+{
+  const err = {
+    code: 'ERR_INVALID_ARG_VALUE',
+    name: 'TypeError',
+    message: /The argument 'hostname' must be a string without null bytes\./,
+  };
+
+  assert.throws(() => dns.lookup('127.0.0.1\u0000.allowed.example', {}), err);
+  assert.throws(() => dnsPromises.lookup('127.0.0.1\u0000.allowed.example', {}), err);
+}
+
 // This also verifies different expectWarning notations.
 common.expectWarning({
   // For 'internal/test/binding' module.
index 05a565463099ec9b222f0bf45a99a396e105da2e..a5b54b490b367ddf8c8faa683cf4b3a555855903 100644 (file)
@@ -25,3 +25,16 @@ const net = require('net');
     });
   });
 }
+
+{
+  assert.throws(() => {
+    net.createConnection({
+      host: '127.0.0.1\u0000.allowed.example',
+      port: 8080,
+    });
+  }, {
+    code: 'ERR_INVALID_ARG_VALUE',
+    name: 'TypeError',
+    message: /The property 'options\.host' must be a string without null bytes\./,
+  });
+}